//spi--sd
#include "spi_sd.h"
#include "unione_lite.h"
#include "ftsdc021.h"
#include "DrvUART010.h"
#include "ssp.h" 
#include "gpio.h"

#include "rtthread.h"

#define SPI_SD_BASE SSP_FTSSP010_1_PA_BASE
#define SPI_HIGHSPEED_VALUE  4
#define SPI_LOWSPEED_VALUE   256

#define CS_GPIO   16

#define SD_CS_ENABLE() do{ uni_set_gpio_value(CS_GPIO,0); }while(0)
#define SD_CS_DISABLE() do{ uni_set_gpio_value(CS_GPIO,1); }while(0)
	
enum SPI_SPEED{
	SPI_SPEED_LOW,
	SPI_SPEED_HIGH
};	
enum BUS_STATUS{
	NO_RELEASE,
	RELEASE
};
typedef enum __SD_TYPE{
	SD_TYPE_V2HC,
	SD_TYPE_V2,
	SD_TYPE_MMC,
	SD_TYPE_ERROR
}SD_TYPE;

SD_TYPE SD_Type = SD_TYPE_ERROR;

static u8  SPI_ReadWriteByte(u8 TxData)
{  
    int count = 0;
	while ((fLib_ReadSSPStatus(SPI_SD_BASE) & 0x2) != 0x2);
	{
		count++;
		if(count>0xffff){
			 //fLib_SetSSP_Reset(SPI_SD_BASE);
			 fLib_printf("write status is busy\r\n");
			 return -1;
	    }
	}
	count = 0;
	fLib_WriteSSP(SPI_SD_BASE, TxData);
	while ((fLib_ReadSSPStatus(SPI_SD_BASE) & 0x03f0) == 0x0);
	{
		count++;
	    if(count>0xffff){
			 //fLib_SetSSP_Reset(SPI_SD_BASE);
			 fLib_printf("read fifo is empty\r\n");
			 return -2;
	    }	
	}
	return fLib_ReadSSP(SPI_SD_BASE);
}

static u8  SD_SendCommand(u8 cmd,u32 arg,u8 crc)
{
   unsigned char r1;
   unsigned int Retry = 0;
   SD_CS_DISABLE();
   //发送8个时钟，提高兼容性
   SPI_ReadWriteByte(0xff);	
   //选中SD卡
   SD_CS_ENABLE();		
   /*按照SD卡的命令序列开始发送命令 */
   //cmd参数的第二位为传输位，数值为1，所以或0x40  
   SPI_ReadWriteByte(cmd | 0x40);    
   //参数段第24-31位数据[31..24]
   SPI_ReadWriteByte((u8)(arg >> 24));
   //参数段第16-23位数据[23..16]	
   SPI_ReadWriteByte((u8)(arg >> 16));
   //参数段第8-15位数据[15..8]	
   SPI_ReadWriteByte((u8)(arg >> 8));	
   //参数段第0-7位数据[7..0]
   SPI_ReadWriteByte((u8)arg);    
   SPI_ReadWriteByte(crc);
   //等待响应或超时退出
   while((r1 = SPI_ReadWriteByte(0xFF))==0xFF)
   {
     Retry++;
     if(Retry > 800)	break; 	//超时次数
   }   
   //关闭片选
   SD_CS_DISABLE();		
   //在总线上额外发送8个时钟，让SD卡完成剩下的工作
   SPI_ReadWriteByte(0xFF); 	
   //返回状态值	
   return r1;		
}

static u8 SD_SendCommand_NoDeassert(u8 cmd, u32 arg,u8 crc)
{
   unsigned char r1;
   unsigned int Retry = 0;
   SD_CS_DISABLE();
   //发送8个时钟，提高兼容性
   SPI_ReadWriteByte(0xff);		
   //选中SD卡
   SD_CS_ENABLE();		
   /* 按照SD卡的命令序列开始发送命令 */
   SPI_ReadWriteByte(cmd | 0x40);                      
   SPI_ReadWriteByte((u8)(arg >> 24));
   SPI_ReadWriteByte((u8)(arg >> 16));
   SPI_ReadWriteByte((u8)(arg >> 8));
   SPI_ReadWriteByte((u8)arg);    
   SPI_ReadWriteByte(crc);
   //等待响应或超时退出
   while((r1 = SPI_ReadWriteByte(0xFF))==0xFF)
   {
      Retry++;
      if(Retry > 800)break; 
    }
    return r1;
}

static void SPI_SetSpeed(enum SPI_SPEED speed)
{
	 if(speed == SPI_SPEED_HIGH){
		 fLib_SetSSPClkdiv(SPI_SD_BASE, SPI_HIGHSPEED_VALUE);
	 }else if(speed == SPI_SPEED_LOW){
		 fLib_SetSSPClkdiv(SPI_SD_BASE, SPI_LOWSPEED_VALUE);
     }else{
		  fLib_printf("speed is error\r\n"); 
     }
}

static void SSP_SPI_Init(u32 io_base)
{
	UINT32 	ctrl;

	fLib_SetSSPFrame(io_base, SPI_Format);
	fLib_SetSSPFramePolar(io_base, SSP_FSPO_LOW);
	fLib_SetSSPOPMode(io_base, SSP_OPM_MSST);
	//fLib_SetSSPOPMode(io_base, SSP_OPM_SLMO);
	// SPI mode 0: SCLKPO = 0, SCLKPH = 0
	fLib_SetSSPSCLKPO(io_base, SSP_SCLKPO_LOW);
	fLib_SetSSPSCLKPH(io_base, SSP_SCLKPH_ONECLK);
	fLib_SetSSP_IntMask(io_base, 0x0);

	//write control register 1
	//fLib_SetSSPClkdiv(io_base, 0x1);

	fLib_SetSSPDataLen(io_base, 7);//8bit

	//enable ssp
	fLib_SetSSP_Enable(io_base, 1);
	
	//enable rx and tx,fs1 out
	ctrl = inw(io_base + SSP_CONTROL2);
	//ctrl |= (SSP_RXEN  | SSP_TXEN | (1<<10));
	ctrl |= (SSP_RXEN  | SSP_TXEN);
    outw(io_base + SSP_CONTROL2, ctrl);
	
	//clear FIFO
	fLib_SSPClearTxFIFO(io_base);
	fLib_SSPClearRxFIFO(io_base);
	
	uni_set_gpio_dir(CS_GPIO,1);
	uni_set_gpio_value(CS_GPIO,1);

}

static u8 SD_WaitReady(void)
{
  u8 r1;
  u16 retry=0;
  do
  {
    r1 = SPI_ReadWriteByte(0xFF);
    retry++;
    if(retry==0xffff)
    	return 1; 
  }while(r1!=0xFF); 
  return 0;
}

static u8 SD_ReceiveData(u8 *data, u16 len, u8 release)
{
   u16 retry;
   u8 r1;
   //启动一次传输
   SD_CS_ENABLE();
   retry = 0;										   
   do
   {
      r1 = SPI_ReadWriteByte(0xFF);		
      retry++;
      if(retry>0xffff)  //0xffff次等待后没有应答，退出报错（可多试几次）
      {
        SD_CS_DISABLE();
        return 1;
      }
   }while(r1 != 0xFE);   //等待SD卡发回数据起始令牌0xFE
   
  //跳出循环后，开始接收数据
  while(len--)
  {
     *data = SPI_ReadWriteByte(0xFF);
	 data++;
  }
  //发送2个伪CRC
  SPI_ReadWriteByte(0xFF);
  SPI_ReadWriteByte(0xFF);
  //按需释放总线
  if(release == RELEASE)
  {
    SD_CS_DISABLE();
    SPI_ReadWriteByte(0xFF);
  }											  		
  return 0;
}


u8 SD_WriteSingleBlock(u32 sector, const u8 *data)
{
  	u8 r1 = -1;
  	u16 i;
  	u16 retry;
    //高速模式
  	SPI_SetSpeed(SPI_SPEED_HIGH);
	//如果不是SDHC卡，将sector地址转为byte地址
    if(SD_Type!=SD_TYPE_V2HC)		
  	{
        sector = sector<<9;
  	}
	//写扇区指令
    r1 = SD_SendCommand(SDHCI_WRITE_BLOCK, sector, 0x01);	
  	if(r1 != 0x00)
  	{
		//应答错误，直接返回
		fLib_printf("++++ SDHCI_WRITE_BLOCK r1 is %d\r\n",r1);  
        return r1;  
  	}
	//开始准备数据传输
    SD_CS_ENABLE();	
  	//先发3个空数据，等待SD卡准备好
  	SPI_ReadWriteByte(0xff);
  	SPI_ReadWriteByte(0xff);
  	SPI_ReadWriteByte(0xff);
  	//放起始令牌0xFE
    SPI_ReadWriteByte(0xFE);
    //发一个sector数据
  	for(i=0;i<512;i++)
  	{
     	SPI_ReadWriteByte(*data++);
  	}
  	//发送2个伪CRC校验
  	SPI_ReadWriteByte(0xff);
  	SPI_ReadWriteByte(0xff);
  	//等待SD卡应答
  	r1 = SPI_ReadWriteByte(0xff);
	//如果为0x05说明数据写入成功
  	if((r1&0x1F)!=0x05)		
  	{    
        SD_CS_DISABLE();
		fLib_printf("++++ SPI_ReadWriteByte r1 is 0x%x\r\n",r1); 
     	return r1;
  	}
    //等待操作完成
  	retry = 0;
    //卡自编程时，数据线被拉低
  	while(!SPI_ReadWriteByte(0xff))		
  	{
     	retry++;
     	if(retry>65534)        //如果长时间没有写入完成，退出报错
     	{
        	SD_CS_DISABLE();
			fLib_printf("++++ SPI_ReadWriteByte retry is %d\r\n",retry); 
        	return 1;           //写入超时，返回1
     	}
  	}
    //写入完成，片选置1
  	SD_CS_DISABLE();
  	SPI_ReadWriteByte(0xff);
    return 0;
}
u8 SD_WriteMultiBlock(u32 sector, const u8 *data, u8 count)
{
  	u8 r1 = -1;
  	u16 i;	 		 
  	SPI_SetSpeed(SPI_SPEED_HIGH); 
  	if(SD_Type != SD_TYPE_V2HC)
    {
        sector = sector<<9;
    }
  	if(SD_Type != SD_TYPE_MMC) 
    {
	    //启用ACMD23指令使能预擦除
    	SD_SendCommand(SDHCI_APP_CMD,0,0x01);
		SD_SendCommand(SDHCI_SET_BLOCK_COUNT,count,0x01);//发送指令		 
    }  
    //写多块指令CMD25
    r1 = SD_SendCommand(SDHCI_WRITE_MULTI_BLOCK, sector, 0x01);	
    //应答不正确，直接返回
  	if(r1 != 0x00){
		fLib_printf("++++ SDHCI_WRITE_MULTI_BLOCK r1 is 0x%x\r\n",r1); 
		return r1;
    }		
	//开始准备数据传输
  	SD_CS_ENABLE();
	//放3个空数据让SD卡准备好		
	SPI_ReadWriteByte(0xff);	
  	SPI_ReadWriteByte(0xff); 
    SPI_ReadWriteByte(0xff);  

  	//下面是N个sector循环写入的部分
  	do
  	{
    	//放起始令牌0xFC，表明是多块写入
     	SPI_ReadWriteByte(0xFC);	  
     	//发1个sector的数据
     	for(i=0;i<512;i++)
     	{
           SPI_ReadWriteByte(*data++);
     	}
     	//发2个伪CRC
	    SPI_ReadWriteByte(0xff);
    	SPI_ReadWriteByte(0xff);
        //等待SD卡回应
     	r1 = SPI_ReadWriteByte(0xff);
	    //0x05表示数据写入成功
     	if((r1&0x1F)!=0x05)			
     	{
        	SD_CS_DISABLE();
            fLib_printf("++++ ack r1 is 0x%x\r\n",r1); 
			if(r1 ==0)
				return 1;
        	return r1;
     	}
        //检测SD卡忙信号
     	if(SD_WaitReady()==1)
     	{
           SD_CS_DISABLE();    //长时间写入未完成，退出
		   fLib_printf("++++ SD_WaitReady1\r\n"); 
           return 1;
     	}	   
    } while(--count);
	

    //发送传输结束令牌0xFD
    SPI_ReadWriteByte(0xFD);	
    //等待准备好   
    if(SD_WaitReady()) 		
    {
        SD_CS_DISABLE();
		fLib_printf("++++ SD_WaitReady2\r\n");
        return 1;  
    }
	//rt_thread_delay(10);
    //写入完成，片选置1
    SD_CS_DISABLE();
    SPI_ReadWriteByte(0xff);
    //返回count值，如果写完，则count=0,否则count=未写完的sector数  
    return count;   
}

u8 SD_ReadSingleBlock(u32 sector, u8 *buffer)
{
  u8 r1;
  //高速模式
  SPI_SetSpeed(SPI_SPEED_HIGH);
  if(SD_Type!=SD_TYPE_V2HC)	//如果不是SDHC卡
  {
     sector = sector<<9;	//512*sector即物理扇区的边界对其地址
  }
   r1 = SD_SendCommand(SDHCI_READ_SINGLE_BLOCK, sector, 1);	//发送CMD17 读命令
   if(r1 != 0x00) 
	    return r1; 		   							  
   r1 = SD_ReceiveData(buffer, 512, RELEASE);	//一个扇区为512字节
   if(r1 != 0)
     return r1;   //读数据出错
   else 
     return 0; 		//读取正确，返回0
}

u8 SD_ReadMultiBlock(u32 sector, u8 *buffer, u8 count)
{
  u8 r1 = -1;	 			 
  SPI_SetSpeed(SPI_SPEED_HIGH);
  if(SD_Type != SD_TYPE_V2HC)
  {
      sector = sector<<9;
  } 
  r1 = SD_SendCommand(SDHCI_READ_MULTI_BLOCK, sector, 1);		//读多块命令
  if(r1 != 0x00){
      fLib_printf("++++ SDHCI_READ_MULTI_BLOCK r1 is 0x%x\r\n",r1); 	  
	  return r1;
  }	  
  do
  {
      if(SD_ReceiveData(buffer, 512, NO_RELEASE) != 0x00)
      { 
		  fLib_printf("++++enter SD_ReceiveData NO_RELEASE\r\n");
       	  break;
      }
      buffer += 512;
   }while(--count);		 
  
  SD_SendCommand(SDHCI_STOP_TRANS, 0, 1);	//全部传输完成，发送停止命令
  SD_CS_DISABLE();	//释放总线
  SPI_ReadWriteByte(0xFF);    
  if(count != 0){
	fLib_printf("++++ SDHCI_READ_MULTI_BLOCK count is %d\r\n",count);
    return count;   //如果没有传完，返回剩余个数
  }else{
    return 0;
  }
}

//获取SD卡的CSD信息包括 包括容量和速度信息
//u8 *cid_data	存放CsD的内存至少16个字节
//返回 0:NO_ERR
//		 1:错误														   
u8 SD_GetCSD(u8 *csd_data)
{
  u8 r1;	 
  r1=SD_SendCommand(SDHCI_SEND_CSD, 0, 1);//cmd9
  if(r1==0)
  {
	 r1=SD_ReceiveData(csd_data,16,RELEASE);
  }
  if(r1)
	  return 1;
  else 
	  return 0;
}

//获取SD卡总扇区数
//返回值:0: 取出容量出错
//其他:SD容量(扇区数/512字节)														  
u32 SD_GetSectorCount(void)
{
  u8 csd[16];
  u32 Capacity;  
  u8 n;
  u16 csize;  					    
  //取CSD信息
  if(SD_GetCSD(csd)!=0) return 0;	    
    
  if((csd[0]&0xC0)==0x40)	 //V2.00卡
  {	
	 csize = csd[9] + ((u16)csd[8] << 8) + 1;
	 Capacity = (u32)csize << 10;//得到扇区数		   
  }else//V1.XX卡
   {	
	  n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
	  csize = (csd[8] >> 6) + ((u16)csd[7] << 2) + ((u16)(csd[6] & 3) << 10) + 1;
	  Capacity= (u32)csize << (n - 9);//得到扇区数 
   }
   return Capacity;
}

u8 SPI_SD_Init(void)
{
  u16 i;
  u8 r1;
  u16 retry;
  u8 buff[6];
  SSP_SPI_Init(SPI_SD_BASE);		
  //SD卡初始化时时钟不能超过400KHz
  SPI_SetSpeed(SPI_SPEED_LOW);
  //CS为低电平，片选置低，选中SD卡		
  SD_CS_ENABLE();	
  //纯延时，等待SD卡上电稳定
  for(i=0;i<0xdf00;i++);  //需要根据具体时钟计算
  //先产生至少74个脉冲，让SD卡初始化完成
  for(i=0;i<10;i++)		
  {
   //参数可随便写，经过10次循环，产生80个脉冲
   SPI_ReadWriteByte(0xff); 	
  }
  fLib_printf("++++ begin CMD0\r\n"); 
//-----------------SD卡复位到idle状态----------------
//循环发送CMD0，直到SD卡返回0x01,进入idle状态
//超时则直接退出
 retry=0;
 do
  {
  //发送CMD0，CRC为0x95
  r1=SD_SendCommand(SDHCI_GO_IDLE_STATE,0,0x95);		
  retry++;
  }
  while((r1!=0x01)&&(retry<200));
  //跳出循环后，检查跳出原因，
  if(retry==200)	//说明已超时	
  {
    return 1;
  }
  
  //fLib_printf("++++ end CMD0\r\n");
  //如果未超时，说明SD卡复位到idle结束
  //发送CMD8命令，获取SD卡的版本信息
  r1=SD_SendCommand(SDHCI_SEND_IF_COND,0x1aa,0x87);
  
  //fLib_printf("++++ end CMD8 is %d\r\n",r1);
  //下面是SD2.0卡的初始化		
  if(r1==0x01)	
  {
    // V2.0的卡，CMD8命令后会传回4字节的数据，要跳过再结束本命令
    buff[0] = SPI_ReadWriteByte(0xFF);  	
    buff[1] = SPI_ReadWriteByte(0xFF);  	
    buff[2] = SPI_ReadWriteByte(0xFF);  	
    buff[3] = SPI_ReadWriteByte(0xFF);  		    
    SD_CS_DISABLE();
    //多发8个时钟	  
    SPI_ReadWriteByte(0xFF);			 
    retry = 0;
    //发卡初始化指令CMD55+CMD41(ACMD41)
    do
    {
      r1 = SD_SendCommand(SDHCI_APP_CMD, 0, 1);//1-->0	
      //fLib_printf("++++ end cmd55 is %d\r\n",r1);	  
      //应返回0x01
      if(r1!=0x01)		
      return r1;	   
      r1 = SD_SendCommand(SDHCI_SD_SEND_OP_COND, 0x40000000, 1);
      //fLib_printf("++++ end cmd41 is %d\r\n",r1);		  
      retry++;
      if(retry>200)	
      return r1; 
    }while(r1!=0);
    //初始化指令发送完成，接下来获取OCR信息	   
    //----------鉴别SD2.0卡版本开始-----------
    //读OCR指令
    r1 = SD_SendCommand_NoDeassert(SD_CMD_READ_OCR, 0, 0);	
    //fLib_printf("++++ end SD_CMD_READ_OCR is %d\r\n",r1);	
    //如果命令没有返回正确应答，直接退出，返回应答
    if(r1!=0x00)  
    return r1;  		 
    //应答正确后，会回传4字节OCR信息
    buff[0] = SPI_ReadWriteByte(0xFF);
    buff[1] = SPI_ReadWriteByte(0xFF); 
    buff[2] = SPI_ReadWriteByte(0xFF); 
    buff[3] = SPI_ReadWriteByte(0xFF);
    //OCR接收完成，片选置高
    SD_CS_DISABLE();
    SPI_ReadWriteByte(0xFF);
    //检查接收到的OCR中的bit30位（CSS），确定其为SD2.0还是SDHC
    //CCS=1：SDHC   CCS=0：SD2.0
	//fLib_printf("++++ buff[0] is 0x%x\r\n",buff[0]);  
	
    if(buff[0]&0x40)
    {
      SD_Type = SD_TYPE_V2HC;
    }   	 
    else		
    {
      SD_Type = SD_TYPE_V2;
    }	
    fLib_printf("SPI_SD_Init success, SD_Type is %d\r\n",SD_Type);  
    //-----------鉴别SD2.0卡版本结束----------- 
    SPI_SetSpeed(SPI_SPEED_HIGH); 		//设置SPI为高速模式
 }
  return 0;
}
